package com.itheima.advice;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.itheima.exception.expections.*;
import com.itheima.exception.utils.LocaleUtils;
import com.itheima.exception.ValidatorException;
import com.itheima.exception.statuscode.AppCode;
import com.itheima.exception.statuscode.ResultCode;
import com.itheima.exception.model.Result;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;

import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.crypto.BadPaddingException;

import java.util.List;
import java.util.Set;

/**
 * GlobalExceptionHandler
 * @author u0035530
 * @since 2020-10-08
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * description: alreadyExists
     * Handler AlreadyExistsException
     * @param ex AlreadyExistsException
     * @return {@link Result< Void>}
     */
    @ExceptionHandler
    @ResponseStatus(HttpStatus.OK)
    public Result<Void> alreadyExists(AlreadyExistsException ex) {
        log.error(ex.getMessage(), ex);
        String message = LocaleUtils.getMessage("message.alreadyExists", ex.getLabel());
        return Result.failed(ResultCode.VALIDATE_ERROR.getCode(), message);
    }

    /**
     * description: business
     * Handler BusinessException
     * @param ex BusinessException
     * @return {@link Result< Void>}
     */
    @ExceptionHandler
    @ResponseStatus(HttpStatus.OK)
    public Result<Void> business(BusinessException ex) {
        log.error(ex.getMessage(), ex);
        return Result.failed(AppCode.APP_ERROR.getCode(), ex.getMessage());
    }



    /**
     * description: forbidden
     * Handler ForbiddenException
     * @param ex ForbiddenException
     * @return {@link Result< Void>}
     */
    @ExceptionHandler
    @ResponseStatus(HttpStatus.OK)
    public Result<Void> forbidden(ForbiddenException ex) {
        log.error(ex.getMessage(), ex);
        String message = LocaleUtils.getMessage("message.noPermission", ex.getMsg());
        return Result.failed(403, message);
    }

    /**
     * description: notFound
     * Handler NotFoundException
     * @param ex NotFoundException
     * @return {@link Result< Void>}
     */
    @ExceptionHandler({NotFoundException.class})
    @ResponseStatus(HttpStatus.OK)
    public Result<Void> notFound(NotFoundException ex) {
        log.error(ex.getMessage(), ex);
        String msg = ex.getResource();
        if (ex.getId() != null) {
            msg += "("+ ex.getId() +")";
        }
        String message = LocaleUtils.getMessage("message.notFound", msg);

        return Result.failed(404, ex.getMessage());
    }

    /**
     * description: methodArgumentNotValid
     * Handler MethodArgumentNotValidException
     * @param ex MethodArgumentNotValidException
     * @return {@link Result< Void>}
     */
    @ExceptionHandler
    @ResponseStatus(HttpStatus.OK)
    public Result<Void> methodArgumentNotValid(MethodArgumentNotValidException ex) {
        log.error(ex.getMessage(), ex);
        List<ObjectError> errors = ex.getBindingResult().getAllErrors();
        StringBuilder sb = new StringBuilder();
        for (ObjectError error : errors) {
            if (sb.length() > 0) {
                sb.append(",");
            }
            sb.append(error.getDefaultMessage());
        }

        return Result.failed(ResultCode.VALIDATE_ERROR.getCode(), sb.toString());
    }

    /**
     * description: constraintViolation
     * Handler ConstraintViolationException
     * @param ex ConstraintViolationException
     * @return {@link Result< Void>}
     */
    @ExceptionHandler
    @ResponseStatus(HttpStatus.OK)
    public Result<Void> constraintViolation(ConstraintViolationException ex) {
        Set<ConstraintViolation<?>> errors = ex.getConstraintViolations();
        StringBuilder sb = new StringBuilder();
        for (ConstraintViolation<?> error : errors) {
            if (sb.length() > 0) {
                sb.append(",");
            }
            sb.append(error.getMessage());
        }

        return Result.failed(ResultCode.VALIDATE_ERROR.getCode(), sb.toString());
    }

    @ExceptionHandler({BindException.class})
    public Result<Void> MethodArgumentNotValidExceptionHandler(BindException e) {
        // 从异常对象中拿到ObjectError对象
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        return Result.failed(ResultCode.VALIDATE_ERROR.getCode(), objectError.getDefaultMessage());
    }

    @ExceptionHandler(APIException.class)
    public Result<Void> APIExceptionHandler(APIException e) {
        log.error("APIException failed, exception! reason：{}", e);
        return Result.failed(e.getCode(), e.getMessage());
    }


    /**
     * 处理校验失败异常 * @param req * @param e * @return
     */
    @ExceptionHandler(value = ValidatorException.class)
    public Result<Void> validatorExceptionHandler(ValidatorException e) {
        log.error("Verification failed, exception! reason：{}", e);
        return Result.failed(ResultCode.VALIDATE_ERROR.getCode(), e.getMessage());
    }


    /**
     * 密钥不对
     * @param e
     * @return
     */
    @ExceptionHandler(value = BadPaddingException.class)
    public Result<Void> exceptionHandler(BadPaddingException e) {
        log.error("BadPaddingException! reason:", e);
        if(StringUtils.isNotEmpty(e.getMessage())){
            return  Result.failed(ResultCode.DECRYPTION_ERROR.getCode(),e.getMessage());
        }
        return  Result.failed(ResultCode.DECRYPTION_ERROR.getCode(),"BadPaddingException! ");
    }


    /**
     * description: internalServerError
     * Handler Exception
     * @param ex Exception
     * @return {@link Result< Void>}
     */
    @ExceptionHandler
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<Void> internalServerError(Exception ex) {
        log.error(ex.getMessage(), ex);
        String message = LocaleUtils.getMessage("message.internalServerError");
        return Result.failed(ResultCode.SERVICE_ERROR.getCode(), "error.internalServerError");
    }





}
